﻿using UnityEngine;
using System.Collections;
using System.Collections.Generic;


public class Wackimals : MonoBehaviour 
{
    public GameObject[] bodyParts; //Should always contain as many parts as bodies length

    private static Wackimals _instance;
    public static Wackimals Instance
    {
        get { return _instance; }
    }

    void Awake()
    {
        if(_instance == null)
        {
            _instance = this;
        }
    }

    public void Create(string axiom, ref List<GameObject> wackimal)
    {
        //Axiom
        char[] parts = axiom.ToCharArray();

        //Parent objects, stacks and types
        GameObject parent = null;
        char parentJoint = '0';
        char parentJointConfiguration = '0';
        Stack<GameObject> stack = new Stack<GameObject>();
        Stack<float> angles = new Stack<float>();
        Stack<ConfigurableJoint> joints = new Stack<ConfigurableJoint>();
        float nextAngle = 0.0f;
        float angle = 0.0f;
        LAST_TYPE lasttype = LAST_TYPE.NONE;

        foreach (char part in parts)
        {
            //Body Parts
            if (char.IsUpper(part))
            {
                //check if it is the first object created
                if (parent == null)
                {
                    int index = System.Array.IndexOf(LSystem.bodies, part);
                    Vector3 spawnPosition = new Vector3(Random.Range(0.0f, 50.0f), 1.0f, Random.Range(0.0f, 50.0f));
                    Camera.main.transform.position = new Vector3(spawnPosition.x, Camera.main.transform.position.y, spawnPosition.z);
                    parent = Instantiate(bodyParts[index], spawnPosition, Quaternion.identity) as GameObject;
                    parent.GetComponent<BodyPart>().Body.renderer.material.color = Color.blue;
                    lasttype = LAST_TYPE.BODY;

                    wackimal.Add(parent);
                }
                else
                {
                    int index = System.Array.IndexOf(LSystem.bodies, part);
                    GameObject g = Instantiate(bodyParts[index], new Vector3(0, 25.5f, 0), Quaternion.identity) as GameObject;
                    Vector3 extraPos = parent.GetComponent<BodyPart>().Direction();
                    g.transform.position = parent.GetComponent<BodyPart>().EndPosition();
                    g.rigidbody.isKinematic = false;

                    if (parentJoint != null)
                    {
                        g.AddComponent<ConfigurableJoint>();
                        ConfigurableJoint joint = g.GetComponent<ConfigurableJoint>();
                        SetJoint(joint, parentJoint);
                        ConfigureJoint(joint, parentJointConfiguration);
                        joint.connectedBody = parent.GetComponent<BodyPart>().ConnectEnd.rigidbody;
                    }

                    g.transform.Rotate(Vector3.up, angle, Space.Self);
                    g.transform.Rotate(Vector3.forward, angle / 2.0f, Space.Self);

                    wackimal.Add(g);
                    parent = g;

                    lasttype = LAST_TYPE.BODY;
                }
            }

            //Joint Setting
            if (char.IsDigit(part))
            {
                if (parent != null)
                {
                    parentJoint = part;
                    lasttype = LAST_TYPE.JOINT;
                }
            }

            //Configurations 
            if (char.IsLower(part))
            {
                if (lasttype == LAST_TYPE.BODY)
                {
                    ConfigureBody(parent, part);
                }
                else if (lasttype == LAST_TYPE.JOINT)
                {
                    parentJointConfiguration = part;
                }
            }

            //New part
            if (char.IsPunctuation(part))
            {
                // Stack
                switch (part)
                {
                    // Brackets
                    case '[':
                        stack.Push(parent);
                        angles.Push(angle);
                        nextAngle += 80.0f;
                        angle = nextAngle;
                        break;
                    case ']':
                        parent = stack.Pop();
                        angle = angles.Pop();
                        break;
                    default:
                        break;
                }
            }
        }
    }

    private void SetJoint(ConfigurableJoint joint, char part)
    {
        joint.xMotion = ConfigurableJointMotion.Locked;
        joint.yMotion = ConfigurableJointMotion.Locked;
        joint.zMotion = ConfigurableJointMotion.Locked;
        joint.angularXMotion = ConfigurableJointMotion.Locked;

        switch (part)
        {
            case '1':
                joint.angularYMotion = ConfigurableJointMotion.Locked;
                joint.angularZMotion = ConfigurableJointMotion.Locked;
                break;
            case '2':
                joint.angularYMotion = ConfigurableJointMotion.Limited;
                joint.angularZMotion = ConfigurableJointMotion.Locked;
                break;
            case '3':
                joint.angularYMotion = ConfigurableJointMotion.Limited;
                joint.angularZMotion = ConfigurableJointMotion.Limited;
                break;
            case '4':
                joint.angularYMotion = ConfigurableJointMotion.Locked;
                joint.angularZMotion = ConfigurableJointMotion.Limited;
                break;
            default:
                Debug.Log("Should not happen!");
                break;
        }
    }

    private void ConfigureBody(GameObject body, char part)
    {
        PhysicsController controller = body.AddComponent<PhysicsController>();

        switch (part)
        {
             case 'a':
                controller.Init(Vector3.zero, new Vector3(0, 45.0f, 0f), 1.0f, 0.0f, 0.0f, ForceMode.Acceleration);
                break;
            case 'b':
                controller.Init(Vector3.zero, new Vector3(0, 67.5f, 35f), 1.0f, 0.0f, 0.0f, ForceMode.Acceleration);
                break;
            case 'c':
                controller.Init(Vector3.zero, new Vector3(0, 90.0f, 0.0f), 1.0f, 0.0f, 0.0f, ForceMode.Acceleration);
                break;
            case 'd':
                controller.Init(Vector3.zero, new Vector3(0, 135.0f, 0.0f), 1.0f, 0.0f, 0.0f, ForceMode.Acceleration);
                break;
            case 'e':
                controller.Init(Vector3.zero, new Vector3(0, 90.0f, 45.0f), 1.0f, 0.0f, 0.0f, ForceMode.Acceleration);
                break;
            case 'f':
                controller.Init(Vector3.up * 2.0f, new Vector3(0, 5.0f, 90.0f), 1.0f, 0.0f, 0.0f, ForceMode.VelocityChange);
                break;
            case 'g':
                controller.Init(Vector3.zero, new Vector3(0, 0, 90f), 2.0f, 0.0f, 0.0f, ForceMode.Acceleration);
                break;
            case 'h':
                controller.Init(Vector3.zero, new Vector3(0, 15.0f, 45.0f), 1.0f, 0.0f, 0.0f, ForceMode.Acceleration);
                break;

            /*case 'a':
                controller.Init(Vector3.zero, new Vector3(0, 35.0f, 35.0f), 1.0f, 0.0f, 0.0f, ForceMode.Acceleration);
                break;
            case 'b':
                controller.Init(Vector3.zero, new Vector3(0, 45.0f, 45.0f), 1.0f, 10.0f, 10.0f, ForceMode.Acceleration);
                break;
            case 'c':
                controller.Init(Vector3.zero, new Vector3(0, 45.0f, 45.0f), 0.8f, 10.0f, 10.0f, ForceMode.Acceleration);
                break;
            case 'd':
                controller.Init(Vector3.zero, new Vector3(0, 55.0f, 55.0f), 1.5f, 5.0f, 5.0f, ForceMode.Acceleration);
                break;
            case 'e':
                controller.Init(Vector3.zero, new Vector3(0, 75.0f, 75.0f), 1.0f, 5.0f, 5.0f, ForceMode.Acceleration);
                break;
            case 'f':
                controller.Init(Vector3.zero, new Vector3(0, 90.0f, 90.0f), 1.5f, 2.0f, 2.0f, ForceMode.Acceleration);
                break;
            case 'g':
                controller.Init(Vector3.zero, new Vector3(0, 120, 120.0f), 2.0f, 15.0f, 15.0f, ForceMode.Acceleration);
                break;
            case 'h':
                controller.Init(Vector3.up * 5.0f, new Vector3(0, 80.0f, 80.0f), 1.5f, 10.0f, 10.0f, ForceMode.VelocityChange);
                break;
            case 'i':
                controller.Init(Vector3.up * 5.0f, new Vector3(0, 0, 0), 1.0f, 25.0f, 25.0f, ForceMode.VelocityChange);
                break;
            case 'j':
                controller.Init(Vector3.up, new Vector3(0, 0, 0), 0.5f, 25.0f, 25.0f, ForceMode.VelocityChange);
                break;
            case 'k':
                controller.Init(Vector3.up * 10, new Vector3(0, 90.0f, 0), 1.5f, 10.0f, 0, ForceMode.VelocityChange);
                break;
            case 'l':
                controller.Init(Vector3.up * 10, new Vector3(0, 0, 90.0f), 1.5f, 10.0f, 0, ForceMode.VelocityChange);
                break;
            case 'm':
                controller.Init(Vector3.zero, new Vector3(0, 65.0f, 65.0f), 0.5f, 10.0f, 10.0f, ForceMode.Acceleration);
                break;
            case 'n':
                controller.Init(Vector3.zero, new Vector3(0, 90.0f, 90.0f), 0.5f, 15.0f, 15.0f, ForceMode.Acceleration);
                break;
            case 'o':
                controller.Init(Vector3.zero, new Vector3(0, 80.0f, 80.0f), 1.0f, 15.0f, 15.0f, ForceMode.Acceleration);
                break;
            case 'p':
                controller.Init(Vector3.up, new Vector3(0, 45.0f, 45.0f), 0.5f, 15.0f, 15.0f, ForceMode.VelocityChange);
                break;
            case 'q':
                controller.Init(Vector3.zero, new Vector3(0, 90.0f, 45.0f), 1.0f, 15.0f, 15.0f, ForceMode.Acceleration);
                break;
            case 'r':
                controller.Init(Vector3.zero, new Vector3(0, 85.0f, 40.0f), 1.0f, 15.0f, 15.0f, ForceMode.Acceleration);
                break;
            case 's':
                controller.Init(Vector3.zero, new Vector3(0, 180.0f, 90.0f), 2.0f, 15.0f, 15.0f, ForceMode.Acceleration);
                break;
            case 't':
                controller.Init(Vector3.zero, new Vector3(0, 75.0f, 75.0f), 1.0f, 45.0f, 45.0f, ForceMode.Acceleration);
                break;
            case 'u':
                controller.Init(Vector3.zero, new Vector3(0, 90.0f, 0.0f), 2.0f, 5.0f, 5.0f, ForceMode.Acceleration);
                break;
            case 'v':
                controller.Init(Vector3.zero, new Vector3(0, 0, 90.0f), 2.0f, 5.0f, 5.0f, ForceMode.Acceleration);
                break;
            case 'x':
                controller.Init(Vector3.down * 2, new Vector3(0, 125.0f, 125.0f), 1.5f, 15.0f, 15.0f, ForceMode.Acceleration);
                break;
            case 'y':
                controller.Init(Vector3.down * 4, new Vector3(0, 135.0f, 135.0f), 1.0f, 15.0f, 15.0f, ForceMode.Acceleration);
                break;
            case 'z':
                controller.Init(Vector3.down * 14, new Vector3(0, 0, 0), 1.0f, 15.0f, 15.0f, ForceMode.Acceleration);
                break;*/
        }
    }

    private void ConfigureJoint(ConfigurableJoint joint, char point)
    {
        bool y = (joint.angularYMotion == ConfigurableJointMotion.Locked) ? false : true;
        bool z = (joint.angularZMotion == ConfigurableJointMotion.Locked) ? false : true;

        SoftJointLimit limitsY = new SoftJointLimit();
        SoftJointLimit limitsZ = new SoftJointLimit();

        switch (point)
        {
            case 'a':
                limitsY.limit = 25.0f;
                limitsY.bounciness = 0.3f;
                limitsZ.limit = 25.0f;
                limitsZ.bounciness = 0.3f;
                break;
            case 'b':
                limitsY.limit = 35.0f;
                limitsY.bounciness = 0.3f;
                limitsZ.limit = 35.0f;
                limitsZ.bounciness = 0.3f;
                break;
            case 'c':
                limitsY.limit = 45.0f;
                limitsY.bounciness = 0.3f;
                limitsZ.limit = 45.0f;
                limitsZ.bounciness = 0.3f;
                break;
            case 'd':
            case 'g':
                limitsY.limit = 75.0f;
                limitsY.bounciness = 0.3f;
                limitsZ.limit = 75.0f;
                limitsZ.bounciness = 0.3f;
                break;
            case 'e':
            case 'h':
                limitsY.limit = 90.0f;
                limitsY.bounciness = 0.3f;
                limitsZ.limit = 90.0f;
                limitsZ.bounciness = 0.3f;
                break;
            case 'f':
                limitsY.limit = 180.0f;
                limitsY.bounciness = 0.3f;
                limitsZ.limit = 180.0f;
                limitsZ.bounciness = 0.3f;
                break;
            
            /*case 'a':
                limitsY.limit = 35.0f;
                limitsY.bounciness = 0.0f;
                limitsZ.limit = 35.0f;
                limitsZ.bounciness = 0.0f;
                break;
            case 'b':
                limitsY.limit = 45.0f;
                limitsY.bounciness = 0.0f;
                limitsZ.limit = 45.0f;
                limitsZ.bounciness = 0.0f;
                break;
            case 'c':
                limitsY.limit = 75.0f;
                limitsY.bounciness = 0.0f;
                limitsZ.limit = 75.0f;
                limitsZ.bounciness = 0.0f;
                break;
            case 'd':
                limitsY.limit = 90.0f;
                limitsY.bounciness = 0.0f;
                limitsZ.limit = 90.0f;
                limitsZ.bounciness = 0.0f;
                break;
            case 'e':
                limitsY.limit = 120.0f;
                limitsY.bounciness = 0.0f;
                limitsZ.limit = 120.0f;
                limitsZ.bounciness = 0.0f;
                break;
            case 'f':
                limitsY.limit = 180.0f;
                limitsY.bounciness = 0.5f;
                limitsZ.limit = 180.0f;
                limitsZ.bounciness = 0.5f;
                break;
            case 'g':
                limitsY.limit = 90.0f;
                limitsY.bounciness = 0.2f;
                limitsZ.limit = 45.0f;
                limitsZ.bounciness = 0.3f;
                break;
            case 'h':
                limitsY.limit = 90.0f;
                limitsY.bounciness = 0.0f;
                limitsZ.limit = 45.0f;
                limitsZ.bounciness = 0.0f;
                break;
            case 'i':
                limitsY.limit = 45.0f;
                limitsY.bounciness = 0.0f;
                limitsZ.limit = 180.0f;
                limitsZ.bounciness = 0.0f;
                break;
            case 'j':
                limitsY.limit = 180.0f;
                limitsY.bounciness = 0.1f;
                limitsZ.limit = 90.0f;
                limitsZ.bounciness = 0.0f;
                break;
            case 'k':
                limitsY.limit = 0.0f;
                limitsY.bounciness = 0.0f;
                limitsZ.limit = 90.0f;
                limitsZ.bounciness = 0.1f;
                break;
            case 'l':
                limitsY.limit = 45.0f;
                limitsY.bounciness = 0.0f;
                limitsZ.limit = 85.0f;
                limitsZ.bounciness = 0.1f;
                break;
            case 'm':
                limitsY.limit = 120.0f;
                limitsY.bounciness = 0.0f;
                limitsZ.limit = 120.0f;
                limitsZ.bounciness = 0.1f;
                break;
            case 'n':
                limitsY.limit = 90.0f;
                limitsY.bounciness = 0.8f;
                limitsZ.limit = 90.0f;
                limitsZ.bounciness = 0.8f;
                break;
            case 'o':
                limitsY.limit = 60.0f;
                limitsY.bounciness = 0.8f;
                limitsZ.limit = 60.0f;
                limitsZ.bounciness = 0.8f;
                break;
            case 'p':
                limitsY.limit = 180.0f;
                limitsY.bounciness = 0.2f;
                limitsZ.limit = 180.0f;
                limitsZ.bounciness = 0.2f;
                break;
            case 'q':
                limitsY.limit = 75.0f;
                limitsY.bounciness = 0.2f;
                limitsZ.limit = 75.0f;
                limitsZ.bounciness = 0.2f;
                break;
            case 'r':
                limitsY.limit = 80.0f;
                limitsY.bounciness = 0.1f;
                limitsZ.limit = 80.0f;
                limitsZ.bounciness = 0.1f;
                break;
            case 's':
                limitsY.limit = 35.0f;
                limitsY.bounciness = 1.0f;
                limitsZ.limit = 35.0f;
                limitsZ.bounciness = 1.0f;
                break;
            case 't':
                limitsY.limit = 45.0f;
                limitsY.bounciness = 0.0f;
                limitsZ.limit = 180.0f;
                limitsZ.bounciness = 0.0f;
                break;
            case 'u':
                limitsY.limit = 10.0f;
                limitsY.bounciness = 0.0f;
                limitsZ.limit = 10.0f;
                limitsZ.bounciness = 0.0f;
                break;
            case 'v':
                limitsY.limit = 120.0f;
                limitsY.bounciness = 0.0f;
                limitsZ.limit = 120.0f;
                limitsZ.bounciness = 0.0f;
                break;
            case 'x':
                limitsY.limit = 120.0f;
                limitsY.bounciness = 0.0f;
                limitsZ.limit = 35.0f;
                limitsZ.bounciness = 0.0f;
                break;
            case 'y':
                limitsY.limit = 35.0f;
                limitsY.bounciness = 0.0f;
                limitsZ.limit = 120.0f;
                limitsZ.bounciness = 0.0f;
                break;
            case 'z':
                limitsY.limit = 75.0f;
                limitsY.bounciness = 1.0f;
                limitsZ.limit = 75.0f;
                limitsZ.bounciness = 1.0f;
                break;*/
        }

        if (y && !z) joint.angularYLimit = limitsY;
        if (!y && z) joint.angularZLimit = limitsZ;
        if (y && z)
        {
            joint.angularYLimit = limitsY;
            joint.angularZLimit = limitsZ;
        }
    }
}
